<?php

class Task {

    public $tags = array();

    public $argv = array();
    public $description = ""; // "Description for ".__CLASS__;
    public $explain = ""; // "Detailed description for ".__CLASS__;
    public $example = ""; // "Example for using the task ".__CLASS__;

    public static $ignored_files = array('.' => 1, '..' => 1, 'CVS' => 1, '.svn' => 1);

    public function __construct($argv) {
        $this->argv = $argv;

        if (empty($this->tags)) { // set the default tag
            $this->tags[] = 'misc';
        }

        $autoTags = split('_', Inflector::underscore(get_class($this)));
        array_pop($autoTags); // get rid of the task tag
        $this->tags = array_merge($this->tags, $autoTags);

        $this->name = implode('_', $autoTags);
    }

    public function main() {
        throw new Exception('Please implement me!');
    }

    public function before() {

    }

    public function after() {

    }

    public static function help($params) {
        if (isset($params['task'])) {
            $taskInstance = self::get_instance($params['task']);
            if (!$taskInstance) {
                return;
            }
        } else if (isset($params['taskInstance'])) {
            $taskInstance = $params['taskInstance'];
        } else {
            return;
        }
        $format = "%20s: %s\n";
        $nonlabelFormat = "%20s  %s\n";
        printf($format, 'Name', $taskInstance->name);
        printf($format, 'Description', $taskInstance->description);
        printf($format, 'Path', __FILE__);

        if (!empty($taskInstance->explain)) {
            $explanations = split("\n", $taskInstance->explain);
            printf($format, 'Explain', array_shift($explainations));
            foreach ($explanations as $exp) {
                printf($nonlabelFormat, '', $exp);
            }
        }

        if (!empty($taskInstance->example)) {
            $examples = split("\n", $taskInstance->example);
            printf($format, 'Examples', array_shift($examples));
            foreach ($examples as $exp) {
                printf($nonlabelFormat, '', $exp);
            }
        }
    }

    public static function get_instance($task, $argv = array()) {
        $taskFileName = $task.'.php';

        // local tasks will override the native mini tasks
        if (defined('APP_TASKS') && file_exists(APP_TASKS.DS.$taskFileName)) {
            require_once(APP_TASKS.DS.$taskFileName);
        } else if (MINI_TASKS.DS.$taskFileName) {
            require_once(MINI_TASKS.DS.$taskFileName);
        } else { // no task file exists
            return false;
        }

        $taskName = Inflector::camelize($task).'Task';
        $taskInstance = new $taskName($argv);
        return $taskInstance;
    }

    public function __toString() {
        return $this->name;
    }

    public static function run($tasks, $argv) {

        $availableTasks = self::get_available_tasks($argv);
        $availableTasksMap = array_flip($availableTasks);
        $tasksToRun = array();

        foreach ($tasks as $task) {
            if (!isset($availableTasksMap[$task])) {
                echo "Task [{$task}] does not exist!\n";
                return false; // check if all of the tasks are valid, if not we don't run any of them
            }

            $tasksToRun[$task] = Task::get_instance($task, $argv);

            // help and list are special, since they provide informational data for other tasks
            if ($task === 'help' || $task === 'list' || $task === 'create_new_project') {
                self::_run($tasksToRun[$task]);
                return true;
            }
        }

        foreach ($tasksToRun as $task => $taskInstance) {
            self::_run($taskInstance);
        }
        return true;
    }

    public static function get_available_tasks($argv) {

        $tasks = array();

        // search for the tasks under mini
        self::_scan_for_tasks(MINI_TASKS, $tasks);

        // if we are under a mini app, try to load it as well
        if (defined('APP_TASKS')) {
            self::_scan_for_tasks(APP_TASKS, $tasks);
        }

        return $tasks;
    }

    private static function _scan_for_tasks($dir, &$tasks) {
        $files = scandir($dir);
        foreach ($files as $file) {
            if (strpos($file, '.php') !== false) {
                $tasks[] = substr($file, 0, -4);
            }
        }
    }

    private static function _run($task) {
        $task->before();
        $task->main();
        $task->after();
    }

    protected static function _copy($from, $to) {
        $format = "%16s  %s\n";
        if (!file_exists($from)) {
            printf($format, 'does not exit', $from);
            return false;
        }

        // in the case of a single file or link
        if (!is_dir($from)) {
            if (!self::_copy_one($from, $to)) {
                printf($format, 'failed create', $from);
                return false;
            }
        } else {
            if (!self::_mkdir($to)) {
                return false;
            }

            // copy the contents from $from to $to
            $files = scandir($from);
            foreach ($files as $file) {
                if (isset(self::$ignored_files[$file])) {
                    continue;
                }

                self::_copy($from.DS.$file, $to.DS.$file);
            }
        }

        return true;
    }

    protected static function _copy_one($from, $to) {
        $format = "%16s  %s\n";
        if (defined('MINI_PRETEND')) {
            if (defined('MINI_SKIP')) {
                if (!file_exists($to)) {
                    printf($format, 'create', $to);
                } else {
                    printf($format, 'skip', $to);
                }
            } else {
                if (!file_exists($to)) {
                    printf($format, 'create', $to);
                } else {
                    printf($format, 'overwrite', $to);
                }
            }
            return true;
        } else {
            if (defined('MINI_SKIP')) {
                if (!file_exists($to)) {
                    if (!copy($from, $to)) {
                        printf($format, 'failed create', $to);
                    } else {
                        printf($format, 'create', $to);
                    }
                } else {
                    printf($format, 'skip', $to);
                }
            } else {
                if (!file_exists($to)) {
                    if (!copy($from, $to)) {
                        printf($format, 'faied create', $to);
                    } else {
                        printf($format, 'create', $to);
                    }
                } else {
                    if (!copy($from, $to)) {
                        printf($format, 'failed create', $to);
                    } else {
                        printf($format, 'overwrite', $to);
                    }
                }
            }
            return true;
        }
    }

    protected static function _mkdir($path) {
        $format = "%16s  %s\n";
        if (file_exists($path)) {
            printf($format, 'exists', $path);
            return true;
        }

        if (!defined('MINI_PRETEND')) {
            if (!mkdir($path)) {
                printf($format, 'failed create', $path);
                return false;
            } else {
                printf($format, 'create', $path);
                return true;
            }
        } else {
            printf($format, 'create', $path);
            return true;
        }
    }
}